﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "GbmpJoinConditionUtils.h"
#include "IDocument.h"
#include "JoinConditionUtils.h"
#include "JoinCutInstanceUtils.h"
#include "IParameterDefinitionLibrary.h"
#include "GcmpBuiltInParameterDefinitions.h"
#include "IGeometryRelationshipComponent.h"
#include "IJoinRelationshipBehavior.h"
#include "IElementPositionPoints.h"
#include "Vector3dUtils.h"
#include "IGraphicsBRepFace.h"
#include "AlgorithmPositionJudge.h"
#include "IPlane.h"
#include "IElementJoin.h"
#include "ICurve3d.h"
#include "ILine3d.h"
#include "ITrimmedSurface.h"
#include "AlgorithmProject.h"
#include "IStructureInstanceJoinBehavior.h"
#include "IInstance.h"
#include "IElementBasicInformation.h"
#include "IRegenerator.h"
#include "IMultiJoinData.h"
#include <algorithm>

#include "GbmpGraphicsNodeUtils.h"
#include "GbmpJoinCutInstanceUtils.h"
#include "IBaseGraphicsElementShapeComponent.h"
#include "AlgorithmBodyTopologyOperate.h"
#include "AlgorithmMinimalDistance.h"

#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;

// 暂时处理JoinUtil废弃警告，后续JoinUtil私有化后把这里恢复

bool gcmp::GbmpJoinConditionUtils::IsSatisfyGeometryJoin(gcmp::IDocument* pDoc, IElementJoin* pJoin)
{
   IElementJoin* pElementJoin = pJoin;
   DBG_WARN_AND_RETURN_FALSE_UNLESS(pElementJoin, L"pElementJoin为空",L"GDMPLab",L"2024-03-30");

   IElementJoinBehavior* pElementJoinBehavior = pJoin->GetElementJoinBehavior();
   IStructureInstanceJoinBehavior* pInstanceJoinBehavior = quick_cast<IStructureInstanceJoinBehavior>(pElementJoinBehavior);
   if (!pInstanceJoinBehavior)
      return false;

   const IInstance* pAttachedInstance = quick_cast<IInstance>(pDoc->GetElement(pElementJoin->GetAttachedId()));
   DBG_WARN_AND_RETURN_FALSE_UNLESS(pAttachedInstance, L"无效pAttachedInstance",L"GDMPLab",L"2024-03-30");

   const IInstance* pAttachingInstance = quick_cast<IInstance>(pDoc->GetElement(pElementJoin->GetAttachingId()));
   DBG_WARN_AND_RETURN_FALSE_UNLESS(pAttachingInstance, L"无效pAttachingInstance",L"GDMPLab",L"2024-03-30");
   if (!AreInstancesHaveVerticalOverlap(pAttachedInstance, pAttachingInstance))
   {
      return false;
   }
   const IElementPositionPoints* pCtrlPtBehavior = pAttachedInstance->GetPositionPoints();
   const IElementPositionPoints* pttachingCtrlPtBehavior = pAttachingInstance->GetPositionPoints();
   if (pCtrlPtBehavior && pttachingCtrlPtBehavior)
   {
      Vector3d ptJoin = Vector3d::Zero;
      if (pElementJoin->GetAttachedPosition() != JoinPosition::Invalid &&
         pElementJoin->GetAttachedPosition() != JoinPosition::Middle)
      {
         ptJoin = pCtrlPtBehavior->GetControlPoint((int32_t)pElementJoin->GetAttachedPosition());
      }
      else if (pElementJoin->GetAttachingPosition() != JoinPosition::Invalid &&
         pElementJoin->GetAttachingPosition() != JoinPosition::Middle)
      {
         ptJoin = pttachingCtrlPtBehavior->GetControlPoint((int32_t)pElementJoin->GetAttachingPosition());
      }
      else
      {
         return false;
      }
      double angle = 0.0;
      if (pElementJoin->GetAttachedPosition() != JoinPosition::Middle && pElementJoin->GetAttachingPosition() != JoinPosition::Middle) //L型连接
      {
         if (!JoinConditionUtils::IsSatisfyLTypeJoinAngleCondition(pAttachingInstance, pAttachedInstance, ptJoin, angle))
            return false;
      }

      if (JoinConditionUtils::IsGeometrySatisfyInstanceJoin(pElementJoin->GetDocument(), pAttachingInstance, pAttachedInstance, ptJoin, pInstanceJoinBehavior->GetJoinType(), true)
         && JoinConditionUtils::IsGeometrySatisfyInstanceJoin(pElementJoin->GetDocument(), pAttachedInstance, pAttachingInstance, ptJoin, pInstanceJoinBehavior->GetJoinType(), false))
      {
         return true;
      }
   }
   return false;
}

bool GbmpJoinConditionUtils::IsInstance1PositionCurveInInstance2EndLine(const IInstance* pInstance1, const IInstance* pInstance2, bool isOnInstance2StartPoint)
{
   double startPointParam = 0;
   bool     isStartPointParamInRange = false;
   double endPointParam = 0;
   bool     isEndPointParamInRange = false;

   const IElementPositionPoints* pCtrlPtBehavior1 = pInstance1->GetPositionPoints();
   Vector3d ptStart1 = pCtrlPtBehavior1 ? pCtrlPtBehavior1->GetControlPoint(0) : Vector3d::Zero;
   Vector3d ptEnd1 = pCtrlPtBehavior1 ? pCtrlPtBehavior1->GetControlPoint(1) : Vector3d::Zero;
   OwnerPtr<ICurve3d> opCurve;
   if (isOnInstance2StartPoint)
   {
      opCurve = GbmpJoinConditionUtils::GetPositionEndLine(pInstance2, true);
   }
   else
   {
      opCurve = GbmpJoinConditionUtils::GetPositionEndLine(pInstance2, false);
   }

   DBG_WARN_AND_RETURN_FALSE_UNLESS(opCurve, L"无效opCurve",L"GDMPLab",L"2024-03-30");
   double dZ = opCurve->GetStartPoint().GetZ();
   ptStart1.SetZ(dZ);
   ptEnd1.SetZ(dZ);
   if (opCurve->GetParameter(ptStart1, startPointParam, isStartPointParamInRange, 1E8)
      && opCurve->GetParameter(ptEnd1, endPointParam, isEndPointParamInRange, 1E8)
      && isStartPointParamInRange
      && isEndPointParamInRange)
   {
      if (pCtrlPtBehavior1 && pCtrlPtBehavior1->GetControlPointCount() > 2) //弧形时
      {
         Vector3d ptMid1 = pCtrlPtBehavior1->GetControlPoint(2);
         double midPointParam = 0;
         bool     isMidPointParamInRange = false;
         if (opCurve->GetParameter(ptMid1, midPointParam, isMidPointParamInRange, 1E8) && isMidPointParamInRange)
         {
            return true;
         }
         else
         {
            return false;
         }
      }
      return true;
   }

   return false;
}

bool gcmp::GbmpJoinConditionUtils::AreInstancesHaveVerticalOverlap(const IInstance* pAttachingInstance, const IInstance* pAttachedInstance)
{
   DBG_WARN_AND_RETURN_UNLESS(pAttachingInstance, false, L"invalid pAttachingInstance",L"GDMPLab",L"2024-03-30");
   DBG_WARN_AND_RETURN_UNLESS(pAttachedInstance, false, L"invalid pAttachedInstance",L"GDMPLab",L"2024-03-30");

   const IElementPositionPoints* pAttachingInstancePositionPts = pAttachingInstance->GetPositionPoints();
   Vector3d ptAttachingOnStart = pAttachingInstancePositionPts ? pAttachingInstancePositionPts->GetControlPoint(0) : Vector3d::Zero;

   const IElementPositionPoints* pAttachedInstancePositionPts = pAttachedInstance->GetPositionPoints();
   Vector3d ptAttachedOnStart = pAttachedInstancePositionPts ? pAttachedInstancePositionPts->GetControlPoint(1) : Vector3d::Zero;

   //XY平面坐标点相等后，再判断深度有没有交集
   double attachingHeight = GbmpJoinCutInstanceUtils::GetHeight(pAttachingInstance);
   double attachedHeight = GbmpJoinCutInstanceUtils::GetHeight(pAttachedInstance);

   Vector3d ptStart1 = ptAttachingOnStart;
   Vector3d ptEnd1 = ptAttachingOnStart + Vector3d::UnitZ * attachingHeight;
   Vector3d ptStart2 = ptAttachedOnStart;
   Vector3d ptEnd2 = ptAttachedOnStart + Vector3d::UnitZ * attachedHeight;
   if (ptStart1.Z() >= (ptEnd2.Z() - Constants::LENGTH_EPS) || ptStart2.Z() >= (ptEnd1.Z() - Constants::LENGTH_EPS))
   {
      return false;
   }
   return true;
}

bool gcmp::GbmpJoinConditionUtils::CanInstancesJoinAtEnd(const IInstance* pAttachingInstance, JoinPosition attachingEnd, const IInstance* pAttachedInstance, JoinPosition attachedEnd)
{
   if (!GbmpJoinCutInstanceUtils::IsJoinable(pAttachingInstance, pAttachedInstance))
      return false;

   const IElementPositionPoints* pAttachedInstancePositionPts = pAttachedInstance->GetPositionPoints();
   const IElementPositionPoints* pAttachingInstancePositionPts = pAttachingInstance->GetPositionPoints();

   Vector3d attachingPnt = pAttachingInstancePositionPts ? pAttachingInstancePositionPts->GetControlPoint((int32_t)attachingEnd) : Vector3d::Zero;
   Vector3d attachedPnt = pAttachedInstancePositionPts ? pAttachedInstancePositionPts->GetControlPoint((int32_t)attachedEnd) : Vector3d::Zero;

   bool pntMet = AreInstancePointAlmostEqual(pAttachingInstance, attachingPnt, pAttachedInstance, attachedPnt);
   if (!pntMet)
      return false;

   return true;
}

bool gcmp::GbmpJoinConditionUtils::AreInstancePointAlmostEqual(const IInstance* pAttachingInstance, const Vector3d& attachingEndPt, const IInstance* pAttachedInstance, const Vector3d& attachedEndPt, double tolerance/*=Constants::DOUBLE_EPS*/)
{
   Vector3d attachingPnt = attachingEndPt;
   Vector3d attachedPnt = attachedEndPt;
   attachingPnt.SetZ(0);
   attachedPnt.SetZ(0);
   bool pntMet = Vector3dUtils::IsEqual(attachingPnt, attachedPnt, tolerance);
   if (!pntMet)
      return false;

   return AreInstancesHaveVerticalOverlap(pAttachingInstance, pAttachedInstance);
}

bool gcmp::GbmpJoinConditionUtils::CanInstancesJoinAtMiddle(const IInstance* pAttachingInstance, JoinPosition attachingEnd, const IInstance* pAttachedInstance)
{
   if (!GbmpJoinCutInstanceUtils::IsJoinable(pAttachingInstance) || !GbmpJoinCutInstanceUtils::IsJoinable(pAttachedInstance))
      return false;

   bool touched = AreInstanceEndPointAndTargetInstanceTouched(pAttachingInstance, attachingEnd, pAttachedInstance);
   if (!touched)
      return false;

   return true;
}

bool gcmp::GbmpJoinConditionUtils::AreInstanceEndPointAndTargetInstanceTouched(const IInstance* pAttachingInstance, JoinPosition attachingEnd, const IInstance* pAttachedInstance)
{
   DBG_WARN_AND_RETURN_UNLESS(pAttachingInstance && pAttachedInstance, false, L"invalid parameter",L"GDMPLab",L"2024-03-30");

   const IElementPositionPoints* pAttachedInstancePositionPts = pAttachedInstance->GetPositionPoints();
   const IElementPositionPoints* pAttachingInstancePositionPts = pAttachingInstance->GetPositionPoints();

   OwnerPtr<ICurve3d> opAttachingCurve = JoinCutInstanceUtils::GetPositionCurve(pAttachingInstance);
   OwnerPtr<ICurve3d> opAttachedCurve = JoinCutInstanceUtils::GetPositionCurve(pAttachedInstance);
   if (opAttachedCurve == nullptr || opAttachingCurve == nullptr)
   {
      return false;
   }

   //1、不允许平行
   //if (IsParallel(opAttachingCurve.get(), opAttachedCurve.get()))
   //    return false;

   //2、判断竖直方向是否有相交可能性
   if (!AreInstancesHaveVerticalOverlap(pAttachingInstance, pAttachedInstance))
   {
      return false;
   }

   Vector3d attachingPnt = pAttachingInstancePositionPts->GetControlPoint((int32_t)attachingEnd);
   Vector3d ptStart = pAttachedInstancePositionPts ? pAttachedInstancePositionPts->GetControlPoint(0) : Vector3d::Zero;
   Vector3d ptEnd = pAttachedInstancePositionPts ? pAttachedInstancePositionPts->GetControlPoint(1) : Vector3d::Zero;
   attachingPnt.SetZ(ptStart.Z());
   double attachedWidth = GbmpJoinCutInstanceUtils::GetWidth(pAttachedInstance);
   double attachingWidth = GbmpJoinCutInstanceUtils::GetWidth(pAttachingInstance);
   //3、端点与端点距离≥最大构件宽度的2/3
   double tolerance = std::max<double>(attachedWidth, attachingWidth) * 2 / 3;
   if (Vector3dUtils::Distance(attachingPnt, ptStart) < tolerance || Vector3dUtils::Distance(attachingPnt, ptEnd) < tolerance) //两靠近端点处不允许建立T型
   {
      return false;
   }

   //4、端点距定位线距离＜对方构件宽度的2/3
   double pointParam = 0;
   bool isParamInRange = false;
   tolerance = attachedWidth * 2 / 3;
   if (!opAttachedCurve->GetParameter(attachingPnt, pointParam, isParamInRange, tolerance) || !isParamInRange)
   {
      return false;
   }

   return true;
}

gcmp::OwnerPtr<gcmp::ICurve3d> gcmp::GbmpJoinConditionUtils::GetPositionEndLine(const IInstance* pInstance, bool isOnStartPoint)
{
   DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pInstance, L"pInstance为空",L"GDMPLab",L"2024-03-30");
   OwnerPtr<gcmp::ICurve3d> opCurve = JoinCutInstanceUtils::GetPositionCurve(pInstance);
   DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opCurve, L"opCurve为空",L"GDMPLab",L"2024-03-30");
   double width = GbmpJoinCutInstanceUtils::GetWidth(pInstance);
   Vector3d ptEndLineStart;
   Vector3d ptEndLineEnd;
   if (isOnStartPoint)
   {
      Vector3d normal = opCurve->GetStartTangent().Cross(Vector3d::UnitZ);
      Vector3d ptStart = opCurve->GetStartPoint();
      ptEndLineStart = ptStart - normal * width / 2;
      ptEndLineEnd = ptStart + normal * width / 2;
   }
   else
   {
      Vector3d normal = opCurve->GetEndTangent().Cross(Vector3d::UnitZ);
      Vector3d ptEnd = opCurve->GetEndPoint();
      ptEndLineStart = ptEnd - normal * width / 2;
      ptEndLineEnd = ptEnd + normal * width / 2;
   }

   return ILine3d::Create(ptEndLineStart, ptEndLineEnd);
}

bool gcmp::GbmpJoinConditionUtils::AdjustInstancePointForTTypeJoin(IInstance* pAttachingInstance, JoinPosition attachingEnd, const IInstance* pAttachedInstance)
{
   if (!GbmpJoinConditionUtils::AreInstanceEndPointAndTargetInstanceTouched(pAttachingInstance, attachingEnd, pAttachedInstance))
   {
      return false;
   }
   if (!GbmpJoinConditionUtils::IsSatisfyJoinTypeAndPositionCondition(pAttachingInstance, attachingEnd, pAttachedInstance, JoinPosition::Middle, JoinType::TType))
   {
      return false;
   }

   IElementPositionPoints* pAttachingInstancePositionPts = pAttachingInstance->GetPositionPoints();

   //确认T型连接没有问题后，对墙的长度做调整(这里只需要调整主动端)
   Vector3d ptOldAttaching = pAttachingInstancePositionPts ? pAttachingInstancePositionPts->GetControlPoint((int32_t)attachingEnd) : Vector3d::Zero;

   OwnerPtr<ICurve3d> opAttachingCurve = JoinCutInstanceUtils::GetPositionCurve(pAttachingInstance);
   OwnerPtr<ICurve3d> opAttachedCurve = JoinCutInstanceUtils::GetPositionCurve(pAttachedInstance);
   OwnerPtr<IPlane> opPlane = IPlane::Create(Vector3d(0, 0, 0), Vector3d::UnitX, Vector3d::UnitY);

   OwnerPtr<ICurve3d> opNewAttachingCurve = AlgorithmProject::ProjectReturnCurve3d(opAttachingCurve.get(), opPlane.get());
   OwnerPtr<ICurve3d> opNewAttachedCurve = AlgorithmProject::ProjectReturnCurve3d(opAttachedCurve.get(), opPlane.get());
   std::vector<Vector3d> intersectPoint;
   if (GbmpGraphicsNodeUtils::IntersectCurve3dsByExtend(opNewAttachingCurve.get(), opNewAttachedCurve.get(), intersectPoint) && intersectPoint.size() > 0)
   {
      Vector3d ptIntersect = intersectPoint[0];
      if (intersectPoint.size() > 1) //两个或者以上时，求距ptOldAttaching最近的一个交点
      {
         double  dMinDist = 1E8;
         for (size_t i = 0; i < intersectPoint.size(); ++i)
         {
            intersectPoint[i].SetZ(ptOldAttaching.GetZ());
            double dDist = Vector3dUtils::Distance(ptOldAttaching, intersectPoint[i]);
            if (dDist <= dMinDist)
            {
               dMinDist = dDist;
               ptIntersect = intersectPoint[i];
            }
         }
      }
      ptIntersect.SetZ(ptOldAttaching.Z());

      Vector3d ptOtherEnd = pAttachingInstancePositionPts ? pAttachingInstancePositionPts->GetControlPoint(0) : Vector3d::Zero;
      if (attachingEnd == JoinPosition::Start)
      {
         ptOtherEnd = pAttachingInstancePositionPts ? pAttachingInstancePositionPts->GetControlPoint(1) : Vector3d::Zero;
      }

      if (Vector3dUtils::IsEqual(ptOtherEnd, ptIntersect, Constants::LENGTH_EPS))
      {
         return false;
      }

      if ((ptOtherEnd - ptIntersect).Length() >= Constants::MAX_MODEL_SIZE)
         return false;

      double oldAttachingLength = JoinCutInstanceUtils::GetLength(pAttachingInstance);
      if (pAttachingInstancePositionPts)
      {
         pAttachingInstancePositionPts->SetControlPoint((int32_t)attachingEnd, ptIntersect);
      }

      double newAttachingLength = JoinCutInstanceUtils::GetLength(pAttachingInstance);
      if (newAttachingLength >= Constants::MAX_MODEL_SIZE || newAttachingLength / oldAttachingLength >= 5)
      {
         if (pAttachingInstancePositionPts)
         {
            pAttachingInstancePositionPts->SetControlPoint((int32_t)attachingEnd, ptOldAttaching);
         }
         return false;
      }

      JoinConditionUtils::CheckInstanceChangedLengthValid(pAttachingInstance->GetBasicInformation()->GetElementId(), oldAttachingLength, newAttachingLength, 1.0 / 5, 5);
      pAttachingInstance->GetDocument()->GetRegenerator()->RegenerateElement(pAttachingInstance->GetElementId());
   }

   //上面先延伸到交点处，若不慢足几何连接，需要回退端点位置
   Vector3d ptJoin = pAttachingInstancePositionPts ? pAttachingInstancePositionPts->GetControlPoint((int32_t)attachingEnd) : Vector3d::Zero;
   if (GbmpJoinConditionUtils::AreInstanceEndPointAndTargetInstanceTouched(pAttachingInstance, attachingEnd, pAttachedInstance)
      && JoinConditionUtils::IsGeometrySatisfyInstanceJoin(pAttachingInstance->GetDocument(), pAttachingInstance, pAttachedInstance, ptJoin, JoinType::TType, true))
   {
      return true;
   }
   else
   {
      if (pAttachingInstancePositionPts)
      {
         pAttachingInstancePositionPts->SetControlPoint((int32_t)attachingEnd, ptOldAttaching);
      }
      pAttachingInstance->GetDocument()->GetRegenerator()->RegenerateElement(pAttachingInstance->GetElementId());

      return false;
   }

   return false;
}

bool gcmp::GbmpJoinConditionUtils::IsSatisfyJoinTypeAndPositionCondition(const IInstance* pAttachingInstance, JoinPosition attachingEnd, const IInstance* pAttachedInstance, JoinPosition attachedEnd, JoinType joinType)
{
   switch (joinType)
   {
   case JoinType::Abut:
   case JoinType::Miter:
   case JoinType::SquareOff:
   {
      // 平接、斜接和方接只能是连接在端点
      if (attachingEnd == JoinPosition::Middle || attachedEnd == JoinPosition::Middle)
         return false;

      bool joinedAtEnd = GbmpJoinConditionUtils::CanInstancesJoinAtEnd(pAttachingInstance, attachingEnd, pAttachedInstance, attachedEnd);
      if (!joinedAtEnd)
         return false;
   }
   break;

   case JoinType::TType:
   {
      bool joinedAtMiddle = GbmpJoinConditionUtils::CanInstancesJoinAtMiddle(pAttachingInstance, attachingEnd, pAttachedInstance);
      if (!joinedAtMiddle)
         return false;

      //T型连接时，连接端须是start/end, 被连接端是middle
      if (attachingEnd == JoinPosition::Middle || attachedEnd != JoinPosition::Middle)
         return false;
   }
   break;

   default:
      DBG_WARN(L"Unsupported join type.",L"GDMPLab",L"2024-03-30");
      return false;
   }
   return true;
}

bool CalculateJoinPointsOnPositionCurve(const gcmp::ICurve3d* pCurve1, const gcmp::ICurve3d* pCurve2, const Vector3d& ptReference, Vector3d& ptInterOnCurve1, Vector3d& ptInterOnCurve2)
{
   DBG_WARN_AND_RETURN_UNLESS(pCurve1, false, L"无效pCurve1",L"GDMPLab",L"2024-03-30");
   DBG_WARN_AND_RETURN_UNLESS(pCurve2, false, L"无效pCurve2",L"GDMPLab",L"2024-03-30");
   //中心基线投影相交且有深度相交
   OwnerPtr<IPlane> opPlane = IPlane::Create(Vector3d(0, 0, 0), Vector3d::UnitX, Vector3d::UnitY);

   OwnerPtr<ICurve3d> opProjectCurve1 = AlgorithmProject::ProjectReturnCurve3d(pCurve1, opPlane.get());
   OwnerPtr<ICurve3d> opProjectCurve2 = AlgorithmProject::ProjectReturnCurve3d(pCurve2, opPlane.get());
   DBG_WARN_AND_RETURN_FALSE_UNLESS(opProjectCurve1, L"无效opProjectCurve1",L"GDMPLab",L"2024-03-30");
   DBG_WARN_AND_RETURN_FALSE_UNLESS(opProjectCurve2, L"无效opProjectCurve2",L"GDMPLab",L"2024-03-30");
   std::vector<Vector3d> intersectPoint;
   if (!GbmpGraphicsNodeUtils::IntersectCurve3dsByExtend(opProjectCurve1.get(), opProjectCurve2.get(), intersectPoint))
      return false;

   Vector3d ptPlanReference = ptReference;
   ptPlanReference.SetZ(intersectPoint[0].GetZ());
   Vector3d ptIntersect = JoinConditionUtils::GetNearestPoint(intersectPoint, ptPlanReference);

   ptInterOnCurve1 = ptIntersect;
   ptInterOnCurve2 = ptIntersect;
   double param = 0;
   bool isInParamRange = false;
   if (pCurve1->GetParameter(ptIntersect, param, isInParamRange, 1E8))
   {
      ptInterOnCurve1 = pCurve1->GetPoint(param);
   }

   if (pCurve2->GetParameter(ptIntersect, param, isInParamRange, 1E8))
   {
      ptInterOnCurve2 = pCurve2->GetPoint(param);
   }

   return true;
}

bool gcmp::GbmpJoinConditionUtils::AreInstancesHaveHorizontalOverlap(IInstance* pAttachingInstance, IInstance* pAttachedInstance, JoinPosition attachingEnd, JoinPosition attachedEnd)
{
   DBG_WARN_AND_RETURN_UNLESS(pAttachingInstance, false, L"invalid pAttachingInstance",L"GDMPLab",L"2024-03-30");
   DBG_WARN_AND_RETURN_UNLESS(pAttachedInstance, false, L"invalid pAttachedInstance",L"GDMPLab",L"2024-03-30");

   IElementPositionPoints* pAttachingInstancePositionPts = pAttachingInstance->GetPositionPoints();
   IElementPositionPoints* pAttachedInstancePositionPts = pAttachedInstance->GetPositionPoints();
   if (!pAttachingInstancePositionPts || !pAttachedInstancePositionPts)
      return false;

   //根据连接位置获取控制点
   Vector3d ptAttachingPos = pAttachingInstancePositionPts->GetControlPoint((int32_t)attachingEnd);
   Vector3d ptAttachedPos = pAttachedInstancePositionPts->GetControlPoint((int32_t)attachedEnd);

   // 获取定位线
   OwnerPtr<ICurve3d> opAttachingCurve = JoinCutInstanceUtils::GetPositionCurve(pAttachingInstance);
   OwnerPtr<ICurve3d> opAttachedCurve = JoinCutInstanceUtils::GetPositionCurve(pAttachedInstance);
   if (opAttachedCurve == nullptr || opAttachingCurve == nullptr)
      return false;

   double attachingWidth = GbmpJoinCutInstanceUtils::GetWidth(pAttachingInstance);
   double attachedWidth = GbmpJoinCutInstanceUtils::GetWidth(pAttachedInstance);
   //连接点位置是否满足距离条件：端点距定位线距离＜对方（提供定位线的构件）宽度的2/3
   if (!IsSatisfyDistanceCondition(ptAttachingPos, opAttachedCurve.get(), attachedWidth * 2 / 3)
      || !IsSatisfyDistanceCondition(ptAttachedPos, opAttachingCurve.get(), attachingWidth * 2 / 3))
   {
      return false;
   }

   double angle = 0.0;
   if (!JoinConditionUtils::IsSatisfyLTypeJoinAngleCondition(pAttachingInstance, pAttachedInstance, ptAttachingPos, angle))
   {
      return false;
   }

   //定位线是否相交，返回在相应定位线上的交点
   Vector3d attachingPtOnPositionCurve, attachedPtOnPositionCurve;
   if (!CalculateJoinPointsOnPositionCurve(opAttachingCurve.get(), opAttachedCurve.get(), ptAttachingPos, attachingPtOnPositionCurve, attachedPtOnPositionCurve))
   {
      return false;
   }

   //不允许退化成点。
   Vector3d anotherPt1 = pAttachingInstancePositionPts->GetControlPoint(attachingEnd == JoinPosition::Start ? (int32_t)JoinPosition::End : (int32_t)JoinPosition::Start);
   if (Vector3dUtils::IsEqual(attachingPtOnPositionCurve, anotherPt1, Constants::LENGTH_EPS))
      return false;

   Vector3d anotherPt2 = pAttachedInstancePositionPts->GetControlPoint(attachedEnd == JoinPosition::Start ? (int32_t)JoinPosition::End : (int32_t)JoinPosition::Start);
   if (Vector3dUtils::IsEqual(attachedPtOnPositionCurve, anotherPt2, Constants::LENGTH_EPS))
      return false;

   //连接位置投影相等且要有深度相交
   if (!GbmpJoinConditionUtils::AreInstancePointAlmostEqual(pAttachingInstance, attachingPtOnPositionCurve, pAttachedInstance, attachedPtOnPositionCurve))
      return false;

   //调整控制点
   Vector3d ptOldAttached = pAttachedInstancePositionPts->GetControlPoint((int32_t)attachedEnd);
   double oldAttachingLength = JoinCutInstanceUtils::GetLength(pAttachingInstance);
   double oldAttachedLength = JoinCutInstanceUtils::GetLength(pAttachedInstance);
   pAttachingInstancePositionPts->SetControlPoint((int32_t)attachingEnd, attachingPtOnPositionCurve);
   pAttachedInstancePositionPts->SetControlPoint((int32_t)attachedEnd, attachedPtOnPositionCurve);

   double newAttachingLength = JoinCutInstanceUtils::GetLength(pAttachingInstance);
   double newAttachedLength = JoinCutInstanceUtils::GetLength(pAttachedInstance);
   double diffAttachingLength = fabs(newAttachingLength - oldAttachingLength);
   double diffAttachedLength = fabs(newAttachedLength - oldAttachedLength);
   double maxDiffLength = 400;  //连接后端点长度偏移变化，小于400mm
   if (diffAttachingLength >= maxDiffLength ||
      diffAttachedLength >= maxDiffLength ||
      newAttachingLength >= Constants::MAX_MODEL_SIZE ||
      newAttachedLength >= Constants::MAX_MODEL_SIZE ||
      newAttachingLength / oldAttachingLength >= 3 ||
      newAttachedLength / oldAttachedLength >= 3)
   {
      if (pAttachingInstancePositionPts)
      {
         pAttachingInstancePositionPts->SetControlPoint((int32_t)attachingEnd, ptAttachingPos);
      }
      if (pAttachedInstancePositionPts)
      {
         pAttachedInstancePositionPts->SetControlPoint((int32_t)attachedEnd, ptOldAttached);
      }

      return false;
   }

   //不允许一个构件的定位线在另一个构件端面之间
   if (GbmpJoinConditionUtils::IsInstance1PositionCurveInInstance2EndLine(pAttachingInstance, pAttachedInstance, attachedEnd == JoinPosition::Start)
      || GbmpJoinConditionUtils::IsInstance1PositionCurveInInstance2EndLine(pAttachedInstance, pAttachingInstance, attachingEnd == JoinPosition::Start))
   {
      if (pAttachingInstancePositionPts)
      {
         pAttachingInstancePositionPts->SetControlPoint((int32_t)attachingEnd, ptAttachingPos);
      }
      if (pAttachedInstancePositionPts)
      {
         pAttachedInstancePositionPts->SetControlPoint((int32_t)attachedEnd, ptOldAttached);
      }

      return false;
   }

   JoinConditionUtils::CheckInstanceChangedLengthValid(pAttachingInstance->GetBasicInformation()->GetElementId(), oldAttachingLength, newAttachingLength, 1.0 / 5, 3);
   JoinConditionUtils::CheckInstanceChangedLengthValid(pAttachingInstance->GetBasicInformation()->GetElementId(), oldAttachedLength, newAttachedLength, 1.0 / 5, 3);

   pAttachingInstance->GetDocument()->GetRegenerator()->RegenerateElement(pAttachingInstance->GetElementId());
   pAttachedInstance->GetDocument()->GetRegenerator()->RegenerateElement(pAttachedInstance->GetElementId());

   IDocument* pDoc = pAttachingInstance->GetDocument();
   //连接几何检测是否允许相连
   if (JoinConditionUtils::IsGeometrySatisfyInstanceJoin(pDoc, pAttachingInstance, pAttachedInstance, attachingPtOnPositionCurve, JoinType::Abut, true) &&
      JoinConditionUtils::IsGeometrySatisfyInstanceJoin(pDoc, pAttachedInstance, pAttachingInstance, attachedPtOnPositionCurve, JoinType::Abut, false))
   {
      return true;
   }
   else
   {
      if (pAttachingInstancePositionPts)
      {
         pAttachingInstancePositionPts->SetControlPoint((int32_t)attachingEnd, ptAttachingPos);
      }
      if (pAttachedInstancePositionPts)
      {
         pAttachedInstancePositionPts->SetControlPoint((int32_t)attachedEnd, ptOldAttached);
      }

      pAttachingInstance->GetDocument()->GetRegenerator()->RegenerateElement(pAttachingInstance->GetElementId());
      pAttachedInstance->GetDocument()->GetRegenerator()->RegenerateElement(pAttachedInstance->GetElementId());
      return false;
   }

   return true;
}

bool gcmp::GbmpJoinConditionUtils::HasJoinOnEnd(const IDocument* pDoc, const ElementId& id1, JoinPosition joinPos1, const ElementId& id2)
{
   DBG_WARN_AND_RETURN_FALSE_UNLESS(pDoc, L"无效pDoc",L"GDMPLab",L"2024-03-30");
   if (!id1.IsValid() || !id2.IsValid())
      return false;

   IElement* pElement1 = pDoc->GetElement(id1);
   if (pElement1 == nullptr)
      return false;

   const IGeometryRelationshipComponent* pGeometryRepCpt = pElement1->GetGeometryRelationshipComponent();
   if (!pGeometryRepCpt
      || !pGeometryRepCpt->GetJoinRelationship())
   {
      return false;
   }

   const IJoinRelationshipBehavior* pJoineHelper = pGeometryRepCpt->GetJoinRelationship();

   ElementId joinId;
   if (joinPos1 == JoinPosition::Start)
   {
      const std::vector<ElementId>& startJoins = pJoineHelper->GetElementJoinIdsAtStart();
      DBG_WARN_AND_RETURN_FALSE_UNLESS(startJoins.size() <= 1, L"默认的连接不支持端点处多个连接",L"GDMPLab",L"2024-03-30");
      if (startJoins.size() == 1)
         joinId = startJoins.at(0);
   }
   else if (joinPos1 == JoinPosition::End)
   {
      const std::vector<ElementId>& endJoins = pJoineHelper->GetElementJoinIdsAtEnd();
      DBG_WARN_AND_RETURN_FALSE_UNLESS(endJoins.size() <= 1, L"默认的连接不支持端点处多个连接",L"GDMPLab",L"2024-03-30");
      if (endJoins.size() == 1)
         joinId = endJoins.at(0);
   }

   const IElementJoin* pJoin = quick_cast<IElementJoin>(pDoc->GetElement(joinId));
   if (pJoin != nullptr)
   {
      if (pJoin->GetAttachingId() == id2 || pJoin->GetAttachedId() == id2)
      {
         return true;
      }

      if (const IMultiJoinData* pMultiJoinData = pJoin->GetMultiJoinData())
      {
         const std::vector<ElementId>& secondaryJoinedElements = pMultiJoinData->GetSecondaryAttachingElementIds();
         if (std::find(secondaryJoinedElements.begin(), secondaryJoinedElements.end(), id2) != secondaryJoinedElements.end())
         {
            return true;
         }
      }
   }

   return false;
}

bool gcmp::GbmpJoinConditionUtils::HasJoinOnEnd(const IDocument* pDoc, const ElementId& id1, JoinPosition joinPos1, const ElementId& id2, JoinPosition joinPos2)
{
   if (joinPos1 == JoinPosition::Start || joinPos1 == JoinPosition::End)
   {
      return HasJoinOnEnd(pDoc, id1, joinPos1, id2);
   }

   if (joinPos2 == JoinPosition::Start || joinPos2 == JoinPosition::End)
   {
      return HasJoinOnEnd(pDoc, id2, joinPos2, id1);
   }
   return false;
}

bool gcmp::GbmpJoinConditionUtils::IsSatisfyLTypeJoin(gcmp::IDocument* pDoc, gcmp::ElementId pAttachingInstanceId, JoinPosition attachingEnd, gcmp::ElementId pAttachedInstanceId, JoinPosition attachedEnd)
{
   DBG_WARN_AND_RETURN_UNLESS(pDoc, false, L"无效pDoc",L"GDMPLab",L"2024-03-30");
   IInstance* pAttachingInstance = quick_cast<IInstance>(pDoc->GetElement(pAttachingInstanceId));
   IInstance* pAttachedInstance = quick_cast<IInstance>(pDoc->GetElement(pAttachedInstanceId));
   DBG_WARN_AND_RETURN_UNLESS(pAttachingInstance, false, L"无效pAttachingInstance",L"GDMPLab",L"2024-03-30");
   DBG_WARN_AND_RETURN_UNLESS(pAttachedInstance, false, L"无效pAttachedInstance",L"GDMPLab",L"2024-03-30");

   IElementPositionPoints* pAttachedInstancePositionPts = pAttachedInstance->GetPositionPoints();
   IElementPositionPoints* pAttachingInstancePositionPts = pAttachingInstance->GetPositionPoints();

   OwnerPtr<ICurve3d> opAttachingCurve = JoinCutInstanceUtils::GetPositionCurve(pAttachingInstance);
   OwnerPtr<ICurve3d> opAttachedCurve = JoinCutInstanceUtils::GetPositionCurve(pAttachedInstance);
   if (opAttachedCurve == nullptr || opAttachingCurve == nullptr)
      return false;

   // 过滤掉平行的情况
   if (IsParallel(pAttachingInstance, pAttachedInstance))
      return false;

   // Instance高度上是否有重合
   if (!AreInstancesHaveVerticalOverlap(pAttachingInstance, pAttachedInstance))
   {
      return false;
   }
   // Instance在水平方向是否满足连接条件
   if (!GbmpJoinConditionUtils::AreInstancesHaveHorizontalOverlap(pAttachingInstance, pAttachedInstance, attachingEnd, attachedEnd))
   {
      return false;
   }

   // 连接类型和位置是否满足要求
   if (!IsSatisfyJoinTypeAndPositionCondition(pAttachingInstance, attachingEnd, pAttachedInstance, attachedEnd, JoinType::Abut))
      return false;
   return true;
}

bool gcmp::GbmpJoinConditionUtils::IsParallel(const IInstance* pAttachingInstance, const IInstance* pAttachedInstance)
{
   OwnerPtr<ICurve3d> pCurve1 = JoinCutInstanceUtils::GetPositionCurve(pAttachingInstance);
   OwnerPtr<ICurve3d> pCurve2 = JoinCutInstanceUtils::GetPositionCurve(pAttachedInstance);
   if (pCurve1 == nullptr || pCurve2 == nullptr)
   {
      return false;
   }

   const ILine3d* pAttachingLine = quick_cast<const ILine3d>(pCurve1.get());
   const ILine3d* pAttachedLine = quick_cast<const ILine3d>(pCurve2.get());
   if (pAttachingLine != nullptr && pAttachedLine != nullptr)
   {
      if (Vector3dUtils::IsParallel(pAttachingLine->GetDirection(), pAttachedLine->GetDirection(), Constants::DOUBLE_EPS))
         return true;
   }
   return false;
}

bool gcmp::GbmpJoinConditionUtils::IsSatisfyDistanceCondition(const Vector3d& ptPos, const ICurve3d* pCurve, double distEpsilon)
{
   DBG_WARN_AND_RETURN_UNLESS(pCurve, false, L"无效pCurve",L"GDMPLab",L"2024-03-30");
   OwnerPtr<IPlane> opPlane = IPlane::Create(Vector3d::Zero, Vector3d::UnitZ);
   DBG_WARN_AND_RETURN_FALSE_UNLESS(opPlane, L"opPlane为空",L"GDMPLab",L"2024-03-30");

   OwnerPtr<ICurve3d> opProjectCurve = AlgorithmProject::ProjectReturnCurve3d(pCurve, opPlane.get());
   DBG_WARN_AND_RETURN_FALSE_UNLESS(opProjectCurve, L"opProjectCurve为空",L"GDMPLab",L"2024-03-30");

   Vector3d projectPoint(ptPos.GetX(), ptPos.GetY(), 0);

   double pointParam = 0.0;
   Vector3d minDisPtOnCurve(Vector3d::Zero);
   double dMinDis = 0.0;
   AlgorithmMinimalDistance::Calculate(opProjectCurve.get(), projectPoint, minDisPtOnCurve, false, &pointParam, dMinDis);
   if (MathUtils::IsLessThan(dMinDis, distEpsilon))
   {
      return true;
   }
   return false;
}


